package bingo.spring.factory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collection;

import net.bingosoft.common.util.ClassUtil;
import net.bingosoft.common.util.ClassUtil.ClassFilter;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import bingo.common.annotation.Autowire;
import bingo.common.annotation.BeanDefination;

/**
 * Mar 11, 2009 4:32:23 AM
 * Author:yangdongyu(yangdy@bingosoft.net)
 * ReadMe:通过类扫描机制动态装配的Bean工厂
 * ChangeLog:
 */
public class AutowireBeanFactory extends AbstractApplicationContext{
	/** Helper class used in event publishing */
	private ApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster();
	private XmlBeanFactory factory;
	private String []pkgNames;
	private String importResource;
	private Resource resource;
	
	

	public AutowireBeanFactory(String pkgNames,String importResource){
		this.pkgNames = pkgNames.split(",");
		this.importResource = importResource;
		this.factory = getBeanFactory(this.pkgNames);
	}


	public XmlBeanFactory getBeanFactory(String... pkgToScan)  {
		Collection<Class<?>> classToProcess = ClassUtil.getClassesOfPackage(new ClassFilter(){
			public boolean accept(Class<?> clazz) {
				return clazz.isAnnotationPresent(BeanDefination.class);
			}

		}, pkgToScan);

		Document doc = DocumentHelper.createDocument();
		Element beans = doc.addElement("beans","http://www.springframework.org/schema/beans");
		Namespace xsiNamespace = new Namespace("xsi","http://www.w3.org/2001/XMLSchema-instance");
		beans.add(xsiNamespace);
		beans.addAttribute(new QName("schemaLocation",xsiNamespace), "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd");
		beans.addAttribute("default-autowire","byName");
		if(this.importResource != null){
			Element importResource = beans.addElement("import");
			importResource.addAttribute("resource", this.importResource);
		}

		for(Class<?> c : classToProcess){
			String beanId = ((BeanDefination)c.getAnnotation(BeanDefination.class)).value();
			if("".equals(beanId)){
				String classname = c.getSimpleName();
				beanId = Character.toLowerCase(classname.charAt(0)) + classname.substring(1);
			}
//			if(!ObjectRegistry.containsBean(beanId)){
				Element bean = beans.addElement("bean");
				bean.addAttribute("id", beanId);
				bean.addAttribute("class", c.getName());
				Class<?> parentClass = c;
				while(parentClass != null && parentClass != Object.class){
					Field [] fields = c.getDeclaredFields();
					for(Field field : fields){
						if(field.isAnnotationPresent(Autowire.class)){
							String propertyValueOrRef = ((Autowire)field.getAnnotation(Autowire.class)).value();
							if("".equals(propertyValueOrRef)){
								propertyValueOrRef = field.getName();
							}
							Element property = bean.addElement("property");
							property.addAttribute("name",field.getName());
							if(field.getType() == String.class ||ClassUtil.isPrimitiveOrWrapper(field.getType())){
								property.addAttribute("value",propertyValueOrRef);
							}else{
								property.addAttribute("ref",propertyValueOrRef);
							}
						}
					}
					parentClass = parentClass.getSuperclass();
				}
//			}
		}

		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setEncoding("UTF-8");
		byte[] newbuf = null;
		XMLWriter writer = null;
		ByteArrayOutputStream baos = null;
		try {
			baos = new ByteArrayOutputStream();
			writer = new XMLWriter(baos, format);
			writer.write(doc);
			writer.flush();
			newbuf = baos.toByteArray();
			if(logger.isDebugEnabled()){
				logger.debug("根据注解动态生成的Spring配置文件的内容为\n" + new String(newbuf));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				if(null != writer){
					writer.close();
				}
				if(null != baos){
					baos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		resource = new ByteArrayResource(newbuf,"根据注解动态生成的Spring配置文件资源");
		XmlBeanFactory factory = new XmlBeanFactory(resource);
		return factory;

	}

	public Resource getResource() {
		return resource;
	}


	@Override
	protected void closeBeanFactory() {
		factory = null;
	}

	@Override
	public ConfigurableListableBeanFactory getBeanFactory()
	throws IllegalStateException {
		return factory;
	}

	@Override
	protected void refreshBeanFactory() throws BeansException,
	IllegalStateException {
		factory = getBeanFactory(pkgNames);
	}

	@Override
	public void publishEvent(ApplicationEvent event) {
		Assert.notNull(event, "Event must not be null");
		if (logger.isDebugEnabled()) {
			logger.debug("Publishing event in context [" + ObjectUtils.identityToString(this) + "]: " + event);
		}
		applicationEventMulticaster.multicastEvent(event);
	}
	public XmlBeanFactory getXmlBeanFactory(){
		return this.factory;
	}
}
